<?php
/**
 * lsys orm
 * @author     Lonely <shan.liu@msn.com>
 * @copyright  (c) 2017 Lonely <shan.liu@msn.com>
 * @license    http://www.apache.org/licenses/LICENSE-2.0
 */
namespace LSYS\ORMCache\Lists\Cache;
use LSYS\ORM;
use LSYS\ORM\Cache;
use LSYS\ORM\Entity;
use LSYS\ORMCache\Lists\CacheSet;
use LSYS\ORMCache\Lists\CacheGet;
use LSYS\ORMCache\Lists\Result;
use LSYS\Config;
use LSYS\ShareRedis;
use LSYS\ShareRedis\RedisShare;

class Redis implements CacheGet,CacheSet, ShareRedis{
	use RedisShare;
	/**
	 * fill add string
	 * @var string
	 */
	public static $add_val=0;
	/**
	 * @var \Redis
	 */
	protected $_redis;
	/**
	 * @var ORM
	 */
	protected $_orm;
	/**
	 * @var Cache
	 */
	protected $_cache;
	/**
	 * @param ORM $orm
	 * @param Config $config
	 */
	public function __construct(ORM $orm,Cache $cache=null,Config $config=null){
		$this->_orm=$orm;
		$this->_cache=$cache===null?Cache::instance():$cache;
		$this->_redis=self::get_share_redis($config);
	}
	/**
	 * @param array $where
	 * @param unknown $sort
	 * @param unknown $offset
	 * @param unknown $limit
	 * @return NULL|\LSYS\ORMCache\Lists\Result
	 */
	public function lists_get(array $where,$sort,$offset,$limit){
		is_null($sort)&&$sort=array();
		is_string($sort)&&$sort=array($sort);
		$ids=$this->_lists_cache_get($where, $sort, $offset, $limit);
		if($ids==null) return null;
		$cache=$this->_cache;
		$orm=$this->_orm;
		$entity=get_class($orm->entity());
		foreach ($ids as $v){//dep item delete update
			if(!$cache->pk_exist_entity($entity,$v)){
				$list_tag=$this->_cache_key_key($where,$sort);
				$this->_redis->zRemoveRangeByScore($list_tag,$offset,$offset+$limit-1);
				$ids=false;
				break;
			}
		}
		if (is_array($ids)) return new Result($orm, $ids,$this->_cache);
		return null;
	}
	/**
	 * @param array $where
	 * @return unknown|number
	 */
	public function lists_total_get(array $where){
		return $this->_lists_cache_total($where);
		if ($total===false){
			$total=$this->_orm->lists_total($where)->count_all();
		}
		return intval($total);
	}
	/**
	 * {@inheritDoc}
	 */
	public function lists_total_set(array $where,$total){
		return $this->_lists_cache_total($where,$total);
	}
	/**
	 * {@inheritDoc}
	 * @see \LSYS\ORM\Lists::lists_add()
	 */
	public function lists_insert(Entity $entity){
		$redis=$this->_redis;
		$hand_tag=$this->_cache_key_hand();
		$tags=$redis->sMembers($hand_tag);
		if(is_array($tags))foreach ($tags as $list_tag){
			$redis->zRemRangeByRank($list_tag,0,0);
			$redis->zAdd($list_tag,0,self::$add_val);
		}
	}
	/**
	 * @param Entity $entity
	 */
	public function lists_delete(Entity $entity){
		$redis=$this->_redis;
		$hand_tag=$this->_cache_key_hand();
		$tags=$redis->sMembers($hand_tag);
		if(is_array($tags))foreach ($tags as $list_tag){
			$redis->zRem($list_tag,$pk);
		}
	}
	public function lists_update(Entity $new_entity){
		$this->lists_delete($entity);
	}
	/**
	 * list total cache
	 * @param number $total
	 * @return number
	 */
	protected function _lists_cache_total(array $where,$total=null){
		$redis=$this->_redis;
		$list_tag_total=$this->_cache_key_key($where,null,true);
		if ($total===null) return $redis->get($list_tag_total);
		else return $redis->set($list_tag_total,$total);
	}
	//list cache key
	private function _cache_key_key($where,$sort=null,$total=false){
		$list_tag=get_class($this->_orm)."-".json_encode($where);
		if (!$total)$list_tag.="-".json_encode($sort);
		return $list_tag;
	}
	//listhand cache key
	protected function _cache_key_hand(){
		return get_class($this->_orm)."_list";
	}
	protected function _cache_key_field(array $fields){
		$key=array();
		foreach ($fields as $field){
			$key[]="field_".$field[0]."_".$field[1];
		}
		return get_class($this->_orm).implode("-",$key);
	}
	/**
	 * @param array $where
	 * @param string $sort
	 * @param int $offset
	 * @param int $limit
	 * @return boolean|array
	 */
	protected function _lists_cache_get(array $where,$sort,$offset,$limit){
		$redis=$this->_redis;
		$total=$this->_lists_cache_total($where);
		if ($total===false) return false;//not cache
		$total=$total>0?intval($total):0;
		if ($offset>=$total) return array();
		$list_tag=$this->_cache_key_key($where,$sort);
		if ($limit===false) $limit=$total;
		$max=$offset+$limit-1;
		if ($max>$total-1) $max=$total-1;
		$count=$redis->zCount($list_tag,$offset,$max);
		if ($count!=$limit) return false;
		$ids= $redis->zRangeByScore($list_tag,$offset,$max);
		if (!is_array($ids)) return false;
		foreach ($ids as $v){
			if ($v==self::$add_val)return false;
		}
		return $ids;
	}
	/**
	 * cache record
	 * @param array $where
	 * @param string $sort
	 * @param number $offset
	 * @param Result $result orm result
	 * @return boolean
	 */
	public function lists_set(array $where,$sort,$offset,$limit,\LSYS\ORM\Result $result){
		$cache=$this->_cache;
		$ids=array();
		foreach ($result as $v){
			if ($cache!=null) $cache->set_entity($v);
			$ids[]=$v->pk();
		}
		$redis=$this->_redis;
		//列表KEY
		$list_tag=$this->_cache_key_key($where,$sort);
		$limit=count($ids);
		if ($limit==0)return false;
		
		
		$oids= $redis->zRangeByScore($list_tag,$offset,$offset+$limit-1);
		if (is_array($oids))foreach ($oids as $v){
			if (strval($v)===self::$add_val){
				$redis->zRemRangeByRank($list_tag,$offset+$limit,$offset+$limit);
				$redis->zAdd($list_tag,$offset+$limit,self::$add_val);
				break;
			}
		}
		
		$start=0;
		$redis->zRemoveRangeByScore($list_tag,$offset,$offset+$limit-1);
		while ($start<$limit){
			$redis->zAdd($list_tag,$offset+$start,$ids[$start]);
			$start++;
		}
		
		$hand_group=$this->_cache_key_hand();
		$redis->sAdd($hand_group,$list_tag);
		return true;
	}
}
